package controllers

import java.io.File
import dao._
import implicits.Implicits._

import javax.inject.Inject
import models.Tables._
import org.joda.time.DateTime
import play.api.i18n.I18nSupport
import play.api.mvc._
import shared.Pojo._
import shared.VarTool
import tool.{FileTool, FormTool, MatrixFileTool, SFormTool, Tool}
import utils.Utils

import scala.collection.SeqMap
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import upickle.default._

/**
 * Created by yz on 23/7/2020
 */
class GroupFileDataController @Inject()(cc: ControllerComponents)(
  implicit val modeDao: ModeDao,
  implicit val fileDataDao: FileDataDao,
  implicit val matrixFileDataDao: MatrixFileDataDao,
  implicit val missionDao: MissionDao,
  implicit val groupFileDataDao: GroupFileDataDao
) extends AbstractController(cc) with MatrixFileTool with I18nSupport {

  import shared.pojo.Pojo.groupFileData._

  def getAll = Action.async { implicit request =>
    val userId = Tool.getUserId
    val fileNameOp = FormTool.fileNameOpForm.bindFromRequest().get.fileName
    val groupNumOp = FormTool.groupNumOpForm.bindFromRequest().get.groupNum
    val groupKind = FormTool.groupKindForm.bindFromRequest().get.groupKind
    val onlyInter = FormTool.onlyInterForm.bindFromRequest().get.onlyInter
    val isFilter=SFormTool.getIsFilterData.isFilter
    val f =if (fileNameOp.isDefined && isFilter) {
      val fileName = fileNameOp.get
      val samplesF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
        if (groupKind == "col") {
          lines.head.drop(1).filterNot { x =>
            List("HMDB", "KEGG", "Metabolite").contains(x)
          }
        } else {
          lines.map(_.head).drop(1)
        }
      }
      if (groupNumOp.isDefined) {
        val groupNum = groupNumOp.get
        samplesF.flatMap { samples =>
          if (groupNum == 3) {
            groupFileDataDao.selectAllMul(userId, samples)
          } else {
            groupFileDataDao.selectAll(userId, samples, groupNum)
          }
        }
      } else {
        samplesF.flatMap { samples =>
          if (onlyInter) {
            groupFileDataDao.selectAllOnlyInter(userId, samples)
          } else {
            groupFileDataDao.selectAll(userId, samples)
          }

        }
      }
    } else {
      groupFileDataDao.selectAll(userId)
    }

    f.map { x =>
      groupFileDataDao.selectAll(userId).map { x =>
        val fileNames = x.map(x => s"${x.fileName}")
        val kindDir = Tool.getUserDataGroupDir(userId)
        kindDir.listFiles().filter {
          file =>
            !fileNames.contains(file.getName)
        }.foreach(_.deleteQuietly)
      }
      val array = Utils.getArrayByTs(x)
      Ok(writeJs(array))
    }
  }

  def all = Action.async { implicit request =>
    val data=Tool.transformRq.transform(reader[GetAll.Request])
    val userId = Tool.getUserId
    val fileNameOp = FormTool.fileNameOpForm.bindFromRequest().get.fileName
    val groupNumOp = FormTool.groupNumOpForm.bindFromRequest().get.groupNum
    val groupKind = FormTool.groupKindForm.bindFromRequest().get.groupKind
    val onlyInter = FormTool.onlyInterForm.bindFromRequest().get.onlyInter
    val isFilter = SFormTool.getIsFilterData.isFilter
    val f = if (fileNameOp.isDefined && isFilter) {
      val fileName = fileNameOp.get
      val samplesF = Tool.produceRawDataLinesF(userId, fileName).map { lines =>
        if (groupKind == "col") {
          lines.head.drop(1).filterNot { x =>
            List("HMDB", "KEGG", "Metabolite").contains(x)
          }
        } else {
          lines.map(_.head).drop(1)
        }
      }
      if (groupNumOp.isDefined) {
        val groupNum = groupNumOp.get
        samplesF.flatMap { samples =>
          if (groupNum == 3) {
            groupFileDataDao.selectAllMul(userId, samples)
          } else {
            groupFileDataDao.selectAll(userId, samples, groupNum)
          }
        }
      } else {
        samplesF.flatMap { samples =>
          if (onlyInter) {
            groupFileDataDao.selectAllOnlyInter(userId, samples)
          } else {
            groupFileDataDao.selectAll(userId, samples)
          }

        }
      }
    } else {
      groupFileDataDao.selectAll(userId)
    }

    f.map { x =>
      groupFileDataDao.selectAll(userId).map { x =>
        val fileNames = x.map(x => s"${x.fileName}")
        val kindDir = Tool.getUserDataGroupDir(userId)
        kindDir.listFiles().filter {
          file =>
            !fileNames.contains(file.getName)
        }.foreach(_.deleteQuietly)
      }
      val array = Utils.getArrayByTs(x)
      Ok(writeJs(array))
    }
  }


  def add = Action.async(parse.multipartFormData) {
    implicit request =>
      val messages = request.messages
      val data = FormTool.groupDataAddForm.bindFromRequest().get
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val userId = Tool.getUserId
      val file = Tool.getInitFile(data, tmpDir, GroupDataKind.defaultFileName)
      val (sampleHeader, classNoteHeader) = data.inputKind match {
        case x if x == messages("UsingTheSampleFile") =>
          ("Sample", "ClassNote")
        case x if x == messages("file") =>
          (data.sampleHeader.get, data.classNoteHeader.get)
      }
      val basicHeaderMap = SeqMap( sampleHeader-> "Sample",  classNoteHeader-> "ClassNote")
      val headerMap = data.pairHeader.map {
        pairHeader =>
          basicHeaderMap ++ SeqMap(pairHeader -> "Pair_ID")
      }.getOrElse {
        basicHeaderMap
      }
      val orderHeaders = headerMap.valueList
      val finalHeaderMap = Tool.headerMapDeal(headerMap)
      val newLines = file.txtLines.convertHeader(finalHeaderMap)
      newLines.toTxtFile(file)
      val myMessage = FileTool.groupFileCheck(file).toMyMessage
      if (!myMessage.valid) {
        Tool.deleteDirectory(tmpDir)
        Future.successful(Ok(ujson.Obj("message" -> myMessage.message, "valid" -> false)))
      } else {
        groupFileDataDao.selectAllFileName(userId).flatMap {
          fileNames =>
            val filePrefix = file.namePrefix
            val newFileName = Tool.getNewFileName(fileNames, file)
            val kindDir = Tool.getUserDataGroupDir(userId)
            val newFile = new File(kindDir, s"${
              newFileName
            }")
            file.txtLines.reOrder(orderHeaders).toTxtFile(newFile)
            val groupMap = newFile.txtLines.lineMap.map {
              map =>
                (map("sample") -> map("classnote"))
            }.toMap
            val row = GroupFileDataRow(id = 0, fileName = s"${
              newFile.getName
            }", userId = userId, tags = data.tags,
              comment = data.comment, uploadTime = new DateTime(), size = file.size.toInt, groupInfo = groupMap)
            groupFileDataDao.insert(row)
        }.map {
          x =>
            Tool.deleteDirectory(tmpDir)
            Ok(ujson.Obj("valid" -> true))
        }
      }
  }

  def getHeaders = Action(parse.multipartFormData) {
    implicit request =>
      val data = FormTool.metDataAddForm.bindFromRequest().get
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val userId = Tool.getUserId
      val file = Tool.getInitFile(data, tmpDir, GroupDataKind.defaultFileName)
      val myMessage = FileTool.groupFileCheckHeader(file).toMyMessage
      if (!myMessage.valid) {
        Tool.deleteDirectory(tmpDir)
        Ok(ujson.Obj("message" -> myMessage.message, "valid" -> false))
      } else {
        val headers = file.txtLines.head
        Tool.deleteDirectory(tmpDir)
        Ok(ujson.Obj("valid" -> true, "headers" -> headers))
      }
  }

  def getNewFileName = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      val userId = Tool.getUserId
      matrixFileDataDao.select(data.id).zip(groupFileDataDao.selectAllFileName(userId)).map {
        case (row, fileNames) =>
          val dataFile = getUserDataKindFile(row.userId, row.fileName)
          val fileName = s"${dataFile.namePrefix}_group.txt"
          val filePrefix = fileName.fileNamePrefix
          val newFileName = Tool.getNewFileName(fileNames, fileName)
          Ok(ujson.Obj("fileName" -> newFileName))
      }
  }

  def detailBefore = Action.async {
    implicit request =>
      val idData = FormTool.idOpForm.bindFromRequest().get
      val userId = Tool.getUserId
      idData.id.map {
        x =>
          Future {
            Ok(views.html.user.data.groupDetail(x))
          }
      }.getOrElse {
        val data = FormTool.fileNameForm.bindFromRequest().get
        val fileName = data.fileName
        val names = fileName.split("/")
        val kind = VarTool.showDirNameMap(names(0)).name
        groupFileDataDao.select(userId, names(1)).map(_.id).map {
          x =>
            Ok(views.html.user.data.groupDetail(x))
        }
      }
  }

  def getFileDataById = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      groupFileDataDao.select(data.id).map {
        x =>
          val json = writeJs(x).toObj ++ ujson.Obj("kind" -> "group")
          Ok(json)
      }
  }

  def getFileData = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      groupFileDataDao.select(data.id).map {
        row =>
          val kindDir = Tool.getUserDataGroupDir(row.userId)
          val file = new File(kindDir, row.fileName)
          val (columnNames, array) = Utils.getInfoByLines(file.txtLines)
          val json = ujson.Obj("columnNames" -> columnNames, "array" -> array)
          Ok(json)
      }
  }

  def deleteFileDataById = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idForm.bindFromRequest().get
      groupFileDataDao.select(data.id).flatMap {
        row =>
          Future {
            val file = Tool.getUserDataGroupFile(row.userId, row.fileName)
            file.deleteQuietly
          }
          groupFileDataDao.delete(userId, data.id).map {
            x =>
              Ok(writeJs("success"))
          }
      }
  }

  def deletes = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.idsForm.bindFromRequest().get
      groupFileDataDao.selectAll(data.ids).flatMap {
        rows =>
          Future {
            rows.foreach {
              row =>
                val file = Tool.getUserDataGroupFile(row.userId, row.fileName)
                file.deleteQuietly
            }
          }
          groupFileDataDao.deletes(userId, data.ids).map {
            x =>
              Ok(writeJs("success"))
          }
      }
  }

  def fileNameNonExistCheck = Action.async {
    implicit request =>
      val data = FormTool.fileNameForm.bindFromRequest.get
      val originalFileNameOp = FormTool.originalFileNameOpForm.bindFromRequest().get.originalFileName
      val userId = Tool.getUserId
      val fileName = data.fileName.addSuffix
      groupFileDataDao.selectAllFileName(userId).map {
        fileNames =>
          val valid = originalFileNameOp.map {
            originalFileName =>
              if (originalFileName == data.fileName) {
                true
              } else {
                !fileNames.contains(fileName)
              }
          }.getOrElse {
            !fileNames.contains(fileName)
          }
          Ok(ujson.Obj("valid" -> valid))
      }
  }

  def update = Action.async {
    implicit request =>
      val id = FormTool.idForm.bindFromRequest().get.id
      val userId = Tool.getUserId
      val data = FormTool.updateForm.bindFromRequest().get
      groupFileDataDao.select(id).flatMap {
        row =>
          val newRow = row.copy(fileName = data.fileName.addSuffix, tags = data.tags, comment = data.comment)
          val kindDir = Tool.getUserDataGroupDir(userId)
          val newFile = new File(kindDir, s"${
            data.fileName.addSuffix
          }")
          new File(kindDir, row.fileName).renameTo(newFile)
          groupFileDataDao.update(newRow).map {
            x =>
              Ok(writeJs("success"))
          }
      }
  }

  def downloadFileById = Action.async {
    implicit request =>
      val data = FormTool.idForm.bindFromRequest().get
      groupFileDataDao.select(data.id).map {
        row =>
          val file = Tool.getUserDataGroupFile(row.userId, row.fileName)
          Ok.sendFile(file).withHeaders(
            CONTENT_DISPOSITION -> Tool.getContentDisposition(s"${
              file.getName
            }"),
            CONTENT_TYPE -> "application/x-download"
          )
      }
  }

  def isPaired = Action {
    implicit request =>
      val data = FormTool.isPairedForm.bindFromRequest().get
      val group=FormTool.groupForm.bindFromRequest().get.group
      val userId = Tool.getUserId
      val groupFile = Tool.getGroupFile(userId, data.groupFile)
      val dataLines = Utils.execFuture(Tool.produceRawDataLinesF(userId, data.file))
      val sampleNames = dataLines.head.drop(1)
      val lines = groupFile.txtLines
      val maps = lines.lineSeqMapLower.filter { map =>
        if (group.isBlank) {
          true
        } else {
          val groups = group.mySplit(" vs ")
          groups.contains(map("classnote"))
        }
      }.filter { map =>
        sampleNames.contains(map("sample"))
      }
      val hasPairID = groupFile.txtLines.head.containsIgnoreCase("Pair_ID")
      val b = if (hasPairID) {
        val groups = maps.groupBy {
          map =>
            map("classnote")
        }
        val hasSampleSize = groups.forall {
          case (classnote, inMaps) =>
            (inMaps.size / maps.size.toDouble) == 0.5
        }
        val isUniq = groups.forall {
          case (classnote, inMaps) =>
            inMaps.map(_ ("pair_id")).isUniq
        }
        val sets = groups.map {
          case (classnote, inMaps) =>
            inMaps.map(_ ("pair_id")).toSet
        }
        val isSame = sets.forall {
          set =>
            set == sets.head
        }
        hasSampleSize && isUniq && isSame
      } else false
      Ok(writeJs(b))
  }

  def getGroups = Action {
    implicit request =>
      val data = FormTool.isPairedForm.bindFromRequest().get
      val userId = Tool.getUserId
      val groupFile = Tool.getGroupFile(userId, data.groupFile)
      val dataFile = Tool.getMatrixFile(userId, data.file)
      val sampleNames = dataFile.txtLines.head.drop(1)
      val lines = groupFile.txtLines
      val maps = lines.lineSeqMapLower.filter { map =>
        sampleNames.contains(map("sample"))
      }
      val groups = maps.map { map =>
        map("classnote")
      }.distinct
      Ok(writeJs(groups))
  }

  def getCovMap = Action {
    implicit request =>
      val data = FormTool.isPairedForm.bindFromRequest().get
      val userId = Tool.getUserId
      val groupFile = Tool.getGroupFile(userId, data.groupFile)
      val dataFile = Tool.getMatrixFile(userId, data.file)
      val sampleNames = dataFile.txtLines.head.drop(1)
      val lines = groupFile.txtLines
      val maps = lines.lineSeqMapLower.filter { map =>
        sampleNames.contains(map("sample"))
      }
      val covMap=lines.head.drop(2).map{columnName=>
        val vs=lines.selectOneColumn(columnName).filter(x=>(!x.equalsIgnoreCase("NA")) && (x.nonBlank))
        val kind=if(vs.forall(_.isInt)){
          "nOrF"
        }else if(vs.forall(_.isDouble)){
          "n"
        }else "f"
        (columnName,kind)
      }.toSeqMap
      Ok(writeJs(covMap))
  }

  def getGroupNum = Action {
    implicit request =>
      val data = FormTool.isPairedForm.bindFromRequest().get
      val userId = Tool.getUserId
      val groupFile = Tool.getGroupFile(userId, data.groupFile)
      val dataLines = Utils.execFuture(Tool.produceRawDataLinesF(userId, data.file))
      val sampleNames = dataLines.head.drop(1)
      val lines = groupFile.txtLines
      val maps = lines.lineSeqMapLower.filter { map =>
        sampleNames.contains(map("sample"))
      }
      val groupNum = maps.map(_ ("classnote")).toSet.size
      Ok(writeJs(groupNum))
  }


}
